/*
 * Copyright 2007-2008 Michele Mostarda ( michele.mostarda@gmail.com ).
 * All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.asemantics.anthill;

import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.MessageEventManager;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

/**
 * This class extends the <code>Message</code> class
 * to define a <i>CommandMessage<(i>.
 *
 * A <i>CommandMessage</i> is a task name and related properties to be executed
 * in a XMPP connected {@link org.asemantics.anthill.CommandListenerTask}.
 */
public abstract class CommandMessage extends AbstractMessage {
    
    /**
     * Name of the property containing the Command fully qualified class type.
     */
    public static final String COMMAND_TYPE = "command_type";

    /**
     * The default value of the message body of a command message.
     */
    public static final String MESSAGE_BODY = "anthill_command_body";

    /**
     * Constructor.
     *
     * @param remoteOperation the associated remote operation.
     */
    protected CommandMessage(Class<? extends RemoteOperation> remoteOperation) {
        super(remoteOperation);

        // Sets the message body to a recognizable non human value.
        setBody(MESSAGE_BODY);

        String className = getClass().getName();
        setProperty(COMMAND_TYPE, className);

        // Add to the message all the notifications requests (offline, delivered, displayed, composing).
        MessageEventManager.addNotificationsRequests(this, true, true, true, false);
    }
 
    /**
     * Takes a low level message and returns an associated {@link CommandMessage} object.
     *
     * @param message
     * @return
     */
    protected static CommandMessage deserializeCommandMessage(Message message) {
        String body = message.getBody();
        if(body == null || ! MESSAGE_BODY.equals(body)) {
            throw new IllegalArgumentException("The command message to be deserialized doesn't seem to be an automatic message.");
        }

        String className = (String) message.getProperty(COMMAND_TYPE);
        if(className == null) {
            throw new IllegalArgumentException("Expected CommandMessage derived type");
        }

        ClassLoader classLoader = CommandMessage.class.getClassLoader();
        Class commandMessageClass;
        try {
            commandMessageClass = classLoader.loadClass(className);
            Constructor<CommandMessage> constructor = commandMessageClass.getConstructor(Map.class);
            CommandMessage commandMessage = constructor.newInstance(getMessageMap(message));
            return commandMessage;
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Cannot load class: "        + className, e);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Cannot access class: "      + className, e);
        } catch (InstantiationException e) {
            throw new IllegalArgumentException("Cannot instantiate class: " + className, e);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Cannot find constructor: "  + className, e);
        } catch (InvocationTargetException e) {
            throw new IllegalArgumentException("Cannot invoke constructor");
        }
    }

}
